﻿/*! @file */
/*
	Copyright (C) 2008, kobake
	Copyright (C) 2018-2022, Sakura Editor Organization

	SPDX-License-Identifier: Zlib
*/
#ifndef SAKURA_CCARET_78A7C1D0_0D47_4FF9_AE73_3421E35ABCB0_H_
#define SAKURA_CCARET_78A7C1D0_0D47_4FF9_AE73_3421E35ABCB0_H_
#pragma once

#include "basis/SakuraBasis.h"
#include "basis/CMySize.h"

#define _CARETMARGINRATE 20
class CTextArea;
class CEditView;
class CEditDoc;
class CTextMetrics;
class CCaret;
class CEditWnd;

class CCaretUnderLine
{
public:
	CCaretUnderLine(CEditView* pcEditView)
	: m_pcEditView(pcEditView)
	{
		m_nLockCounter = 0;
		m_nUnderLineLockCounter = 0;
	}
	// 表示非表示を切り替えられないようにする
	void Lock()
	{
		m_nLockCounter++;
	}
	// 表示非表示を切り替えられるようにする
	void UnLock()
	{
		m_nLockCounter--;
		if (m_nLockCounter < 0){
			m_nLockCounter = 0;
		}
	}
	void UnderLineLock()
	{
		m_nUnderLineLockCounter++;
	}
	// 表示非表示を切り替えられるようにする
	void UnderLineUnLock()
	{
		m_nUnderLineLockCounter--;
		if (m_nUnderLineLockCounter < 0){
			m_nUnderLineLockCounter = 0;
		}
	}
	void CaretUnderLineON(bool bDraw, bool bPaintDraw);	// カーソル行アンダーラインのON
	void CaretUnderLineOFF(bool bDraw, bool bDrawPaint = true, bool bResetFlag = false );	// カーソル行アンダーラインのOFF
	void SetUnderLineDoNotOFF( bool flag ){ if( !m_nLockCounter )m_bUnderLineDoNotOFF = flag; }
	void SetVertLineDoNotOFF( bool flag ){ if( !m_nLockCounter )m_bVertLineDoNotOFF = flag; }
	inline bool GetUnderLineDoNotOFF( )const { return m_bUnderLineDoNotOFF; }
	inline bool GetVertLineDoNotOFF( )const { return m_bVertLineDoNotOFF; }
private:
	/* ロックカウンタ。0のときは、ロックされていない。UnLockが呼ばれすぎても負にはならない */
	int m_nLockCounter;
	int m_nUnderLineLockCounter;
	CEditView* m_pcEditView;
	bool m_bUnderLineDoNotOFF;
	bool m_bVertLineDoNotOFF;
};

class CCaret{
	using Me = CCaret;

public:
	CCaret(CEditView* pEditView, const CEditDoc* pEditDoc);
	CCaret(const Me&) = delete;
	Me& operator = (const Me&) = delete;
	CCaret(Me&&) noexcept = delete;
	Me& operator = (Me&&) noexcept = delete;
	virtual ~CCaret();

	// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- //
	//                         外部依存                            //
	// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- //

	int GetHankakuDx() const;
	int GetHankakuDy() const;
	int GetHankakuHeight() const;

	//ドキュメントのインスタンスを求める
	const CEditDoc* GetDocument() const{ return m_pEditDoc; }

	// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- //
	//                         実装補助                            //
	// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- //
	POINT CalcCaretDrawPos(const CLayoutPoint& ptCaretPos) const;

	// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- //
	//                   初期化・終了処理など                      //
	// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- //

	//! キャレットの作成。2006.12.07 ryoji
	void CreateEditCaret(
		COLORREF crCaret,
		COLORREF crBack,
		int nWidth,
		int nHeight
	);
	
	//! キャレットを破棄する（内部的にも破棄）
	void DestroyCaret()
	{
		::DestroyCaret();
		m_sizeCaret.cx = 0;
	}

	//! コピー
	void CopyCaretStatus(CCaret* pDestCaret) const;

	// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- //
	//                           移動                              //
	// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- //

	//設定
	CLayoutInt MoveCursorToClientPoint( const POINT& ptClientPos, bool = false, CLayoutPoint* = nullptr );		//!< マウス等による座標指定によるカーソル移動
	CLayoutInt Cursor_UPDOWN( CLayoutInt nMoveLines, bool bSelect );	//!< カーソル上下移動処理
	CLayoutInt MoveCursor(												//!< 行桁指定によるカーソル移動
		CLayoutPoint	ptWk_CaretPos,									//!< [in] 移動先レイアウト位置
		bool			bScroll,										//!< [in] true: 画面位置調整有り  false: 画面位置調整無し
		int				nCaretMarginRate	= _CARETMARGINRATE,			//!< [in] 縦スクロール開始位置を決める値
		bool			bUnderlineDoNotOFF	= false,					//!< [in] アンダーラインを消去しない
		bool			bVertLineDoNotOFF	= false						//!< [in] カーソル位置縦線を消去しない
	);
	CLayoutInt MoveCursorFastMode(
		const CLogicPoint&	pptWk_CaretPosLogic							//!< [in] 移動先ロジック位置
	);
	CLayoutInt MoveCursorProperly( CLayoutPoint ptNewXY, bool, bool = false, CLayoutPoint* = nullptr, int = _CARETMARGINRATE, int = 0 );	/* 行桁指定によるカーソル移動（座標調整付き） */

	//$ 設計思想的に微妙
	void SetCaretLayoutPos(const CLayoutPoint& pt){ m_ptCaretPos_Layout = pt; }	//!< キャレット位置(レイアウト)を設定
	void SetCaretLogicPos(const CLogicPoint& pt){ m_ptCaretPos_Logic=pt; }		//!< キャレット位置(ロジック)を設定

	// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- //
	//                        サイズ変更                           //
	// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- //
	void SetCaretSize(int nW, int nH){ m_sizeCaret.Set(nW,nH); }						//!< キャレットサイズを設定

	// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- //
	//                           計算                              //
	// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- //
	//計算
	BOOL GetAdjustCursorPos( CLayoutPoint* pptPosXY ); //!< 正しいカーソル位置を算出する

	void ClearCaretPosInfoCache();

	// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- //
	//                           表示                              //
	// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- //

	//描画？
	void ShowEditCaret();    //!< キャレットの表示・更新
	void ShowCaretPosInfo(); //!< キャレットの行桁位置を表示する

	//API呼び出し
	void ShowCaret_( HWND hwnd );
	void HideCaret_( HWND hwnd );

	// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- //
	//                           取得                              //
	// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- //

	CLayoutPoint GetCaretLayoutPos() const	{ return m_ptCaretPos_Layout; }	//!< キャレット位置(レイアウト)を取得
	CMySize GetCaretSize() const			{ return m_sizeCaret; }			//!< キャレットサイズを取得。※正確には高さは違うらしい (この半分のこともある？)
	bool ExistCaretFocus() const			{ return m_sizeCaret.cx>0; }	//!< キャレットのフォーカスがあるか。※横幅値で判定してるらしい。
	CLogicPoint GetCaretLogicPos() const	{ return m_ptCaretPos_Logic; }	//!< キャレット位置(ロジック)を取得

	// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- //
	//                  低頻度インターフェース                     //
	// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- //
	bool GetCaretShowFlag() const{ return m_bCaretShowFlag; }

	// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- //
	//                        メンバ変数                           //
	// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- //
private:
	//参照
	CEditView*				m_pEditView;
	const CEditDoc*			m_pEditDoc;

	//キャレット位置
	CLayoutPoint	m_ptCaretPos_Layout;	// ビュー左上端からのカーソル位置。レイアウト単位。
	CLogicPoint		m_ptCaretPos_Logic;		// カーソル位置。ロジック単位。データ内文字単位。

public:
	CLayoutInt		m_nCaretPosX_Prev;	// 直前のX座標記憶用。レイアウト単位。このソースの下部に詳細説明があります。

	//キャレット見た目
private:
	CMySize			m_sizeCaret;		// キャレットのサイズ。ピクセル単位。
	COLORREF		m_crCaret;			// キャレットの色				// 2006.12.07 ryoji
	HBITMAP			m_hbmpCaret;		// キャレットのビットマップ		// 2006.11.28 ryoji
	bool			m_bCaretShowFlag;

	//アンダーライン
public:
	mutable CCaretUnderLine m_cUnderLine;
	
	bool			m_bClearStatus;
};

/*!	@brief CCaret::m_nCaretPosX_Prev
	直前のX座標記憶用

	フリーカーソルモードでない場合にカーソルを上下に移動させた場合
	カーソル位置より短い行では行末にカーソルを移動するが，
	さらに移動を続けた場合に長い行で移動起点のX位置を復元できるように
	するための変数．
	
	@par 使い方
	読み出しはCEditView::Cursor_UPDOWN()のみで行う．
	カーソル上下移動以外でカーソル移動を行った場合には
	直ちにm_nCaretPosXの値を設定する．そうしないと
	その直後のカーソル上下移動で移動前のX座標に戻ってしまう．

	ビュー左端からのカーソル桁位置(０開始)
	
	@date 2004.04.09 genta 説明文追加
*/
#endif /* SAKURA_CCARET_78A7C1D0_0D47_4FF9_AE73_3421E35ABCB0_H_ */
